package com.ck.autoconfig.tenant.security;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.extra.servlet.ServletUtil;
import com.ck.autoconfig.tenant.config.TenantProperties;
import com.ck.autoconfig.tenant.context.TenantRequestContext;
import com.ck.autoconfig.tenant.service.TenantService;
import com.ck.common.utils.security.SecurityUtils;
import com.ck.common.vo.LoginUserVo;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.MediaType;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Objects;

/**
 * @author ck
 * @date 2024/4/11 11:01
 * desc:*在安全验证的过程中，校验当前登录的用户是否有足够的权限访问其他租户的数据，以确保数据安全性。
 */
@Slf4j
@RequiredArgsConstructor
public class TenantSecurityWebFilter extends OncePerRequestFilter {
    private final TenantService tenantService;

    private final TenantProperties tenantProperties;

    public TenantSecurityWebFilter(TenantProperties tenantProperties,TenantService tenantService){
        this.tenantProperties = tenantProperties;
        this.tenantService = tenantService;
    }
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        //校验租户
        Long tenantId = TenantRequestContext.getTenantId();
        LoginUserVo loginUser = SecurityUtils.getLoginUser();
        if (loginUser != null){
            // 如果获取不到租户编号，则尝试使用登陆用户的租户编号
            if (tenantId == null) {
                tenantId = loginUser.getUser().getTenantId();
                TenantRequestContext.setTenantId(tenantId);
                // 如果传递了租户编号，则进行比对租户编号，避免越权问题
            } else if (!Objects.equals(loginUser.getUser().getTenantId(), TenantRequestContext.getTenantId())) {
                log.error("[doFilterInternal][租户({}) User({}/{}) 越权访问租户({}) URL({}/{})]",
                        loginUser.getUser().getTenantId(), loginUser.getUser().getUserId(),
                        TenantRequestContext.getTenantId(), request.getRequestURI(), request.getMethod());
                ServletUtil.write(response,"您无权访问该租户的数据", MediaType.APPLICATION_JSON_UTF8_VALUE);
                return;
            }
        }

        // 如果非允许忽略租户的 URL，则校验租户是否合法
        if (!isIgnoreUrl(request)) {
            // 2. 如果请求未带租户的编号，不允许访问。
            if (tenantId == null) {
                log.error("[doFilterInternal][URL({}/{}) 未传递租户编号]", request.getRequestURI(), request.getMethod());
                ServletUtil.write(response, "请求的租户标识未传递，请进行排查", MediaType.APPLICATION_JSON_UTF8_VALUE);
                return;
            }
            // 3. 校验租户是否合法，例如说被禁用、到期
            if (!tenantService.validTenant(tenantId)){
                ServletUtil.write(response, "请求的租户被禁用或到期", MediaType.APPLICATION_JSON_UTF8_VALUE);
                return;
            }
        } else { // 如果是允许忽略租户的 URL，若未传递租户编号，则默认忽略租户编号，避免报错
            if (tenantId == null) {
                TenantRequestContext.setIgnore(true);
            }
        }

        // 继续过滤
        filterChain.doFilter(request, response);
    }
    private boolean isIgnoreUrl(HttpServletRequest request) {
        // 快速匹配，保证性能
        if (CollUtil.contains(tenantProperties.getIgnoreUrls(), request.getRequestURI())) {
            return true;
        }
        AntPathMatcher antPathMatcher = new AntPathMatcher();
        // 逐个 Ant 路径匹配
        for (String url : tenantProperties.getIgnoreUrls()) {
            if (antPathMatcher.match(url, request.getRequestURI())) {
                return true;
            }
        }
        return false;
    }
}
